#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <sys/io.h>
#include <errno.h>
#include "sysMonitor.h"
#include "common.h"
#include "../logServer/clogger.h"
#define MONITORLogDebug(fmt, args...) CLogger(MODULE_SYSMONITOR, __FILE__, __LINE__, LOG_DEBUG, fmt, ##args);
#define MONITORLogInfo(fmt, args...) CLogger(MODULE_SYSMONITOR, __FILE__, __LINE__, LOG_INFO, fmt, ##args);
#define MONITORLogNotice(fmt, args...) CLogger(MODULE_SYSMONITOR, __FILE__, __LINE__, LOG_NOTICE, fmt, ##args);
#define MONITORLogWarning(fmt, args...) CLogger(MODULE_SYSMONITOR, __FILE__, __LINE__, LOG_WARNING, fmt, ##args);
#define MONITORLogError(fmt, args...) CLogger(MODULE_SYSMONITOR, __FILE__, __LINE__, LOG_ERR, fmt, ##args);
#define MONITORLogCrit(fmt, args...) CLogger(MODULE_SYSMONITOR, __FILE__, __LINE__, LOG_CRIT, fmt, ##args);
#define MONITORLogAlert(fmt, args...) CLogger(MODULE_SYSMONITOR, __FILE__, __LINE__, LOG_ALERT, fmt, ##args);
#define MONITORLogEmerg(fmt, args...) CLogger(MODULE_SYSMONITOR, __FILE__, __LINE__, LOG_EMERG, fmt, ##args);


#define IoWrite8(port,data) outb(data,port) 
#define IoRead8(port)	inb(port)
#define FEP_DEBUG 0



bool downLoadPkg = false;
static bool feedWTD = true;
static S32 times = 0; // cheak state per minuter

ModState ModStat[MOD_NUM] = 
{
    { "system", MODULE_SYSTEM, false },
    { "datapro", MODULE_DATAPRO, false },
    { "datam", MODULE_DATAM, false },
    { "pm", MODULE_PM, false },
    { "msgproc", MODULE_MESSAGE, false},
    { "gsm", MODULE_GSM_CAP, false },
    { "cdma", MODULE_CDMA_CAP, false },
    { "pa", MODULE_PA, false },
    { "gps", MODULE_GPS, false }, 
    { "eutran", MODULE_EUTRAN_SNIFFER, false },
    { "fep", MODULE_INTF, false },
    { "sms", MODULE_SMS, false },
    { "fwmgr", MODULE_FWMGR, false },
    { "logServer", MODULE_LOG_SERVER, false},
    { "wcdma", MODULE_WCDMA_CAP, false},
    { "tdscdma", MODULE_TDSCDMA_CAP, false},
	{ "wifi", MODULE_WIFI, false},
    { "scanner", MODULE_SCANNER, false},
	{ "gpsboard", MODULE_GPS_BOARD, false},
    { "wifiboard", MODULE_WIFI_BOARD, false}
};

//static void* pcheckSystemState = NULL;

void initWatchDog()
{
    if (iopl(3)<0){
		perror("iopl");
		return;
    }
//1,打开WDT
	IoWrite8(0x2e,0x87);
    IoWrite8(0x2e,0x01);
	IoWrite8(0x2e,0x55); 
	IoWrite8(0x2e,0x55);//进入MB PNP模式
	
	IoWrite8(0x2e,0x07);
	IoWrite8(0x2f,0x07);//设定07为逻辑设备
	IoWrite8(0x2e,0x71);
	IoWrite8(0x2f,0x00);//打开看门狗

 	IoWrite8(0x2e,0x02);
	IoWrite8(0x2f,0x02);
//2，喂狗
	IoWrite8(0x2e,0x87);
    IoWrite8(0x2e,0x01);
	IoWrite8(0x2e,0x55); 
	IoWrite8(0x2e,0x55);//进入MB PNP模式
	IoWrite8(0x2e,0x07);
	IoWrite8(0x2f,0x07);//设定07为逻辑设备
	
	IoWrite8(0x2e,0x72);
	IoWrite8(0x2f,0xD0);// 偏移值72h，里面写D0h表示看门狗计时单位为秒；为50h，表示看门狗计时器单位为分，开始喂看门狗，
//73 74 为timeout value,如 16秒
	IoWrite8(0x2e,0x73);
	IoWrite8(0x2f,0x78);//写入需要设置的看门狗的值 //timeout is 120s
	IoWrite8(0x2e,0x74);
	IoWrite8(0x2f,0x00);
    
 	IoWrite8(0x2e,0x02);
	IoWrite8(0x2f,0x02);
    MONITORLogNotice("system start watchdog");

}

void feedWatchDog()
{
    IoWrite8(0x2e,0x87);
    IoWrite8(0x2e,0x01);
	IoWrite8(0x2e,0x55); 
	IoWrite8(0x2e,0x55);//进入MB PNP模式
	IoWrite8(0x2e,0x07);
	IoWrite8(0x2f,0x07);//设定07为逻辑设备
	IoWrite8(0x2e,0x73);
	IoWrite8(0x2f,0x78);
 	IoWrite8(0x2e,0x02);
	IoWrite8(0x2f,0x02);
    MONITORLogDebug("feed the  watch dog ");
}

void checkSystemStateTimer( void *h )
{
    S32 i = 0;

    for ( ; i < MOD_NUM; i++ )
    {
        if ( !ModStat[i].ModOk )
        {
            MONITORLogError("modId[0x%x] modName[%s] lost heartbeat ", ModStat[i].ModId, ModStat[i].ModName);
            MONITORLogError( "system should be reboot bescause modName[%s] lost ", ModStat[i].ModName);  
            if ( ModStat[i].ModId == MODULE_FWMGR && downLoadPkg) 
            { 
                MONITORLogError("system should be not reboot bescause modName[%s] is downloading", ModStat[i].ModName);   
                break;
            }
            else if(ModStat[i].ModId == MODULE_INTF)
            {
#ifdef MODULE_TEST
                break;
#endif
                MONITORLogError("fep lost heartbeat so be reboot");
            }
			else if(ModStat[i].ModId == MODULE_SYSTEM)
			{
                MONITORLogError("system module lost heartbeat, just reboot here.");
			    //wait 5 seconds for logserver to log 
                saveImportantLog("system module lost heartbeat so reboot");
#ifndef MODULE_TEST 
			    sleep(5);
			    system("reboot -f");
                return;
#endif
			}
            char buf[256] = {0};
            sprintf(buf, "%s module lost heartbeat so reboot",  ModStat[i].ModName);
            saveImportantLog(buf);
#ifndef MODULE_TEST

            U8 delay = 6;
			//send reboot command to system module
            CM_SendMsg_1Para(MODULE_SYSTEM, MODULE_SYSMONITOR, MSG_TYPE_COMMAND, CMD_SET, CMD_CODE_SYS_REBOOT, 0,
            PARA_TYPE_DELAY,sizeof(U32), (U8 *)&delay);             
            feedWTD = false;
#endif            
            return;
        }
        else
        { 
            ModStat[i].ModOk = false;
            MONITORLogDebug("modId[0x%x] modName[%s] recv  heartbeat", ModStat[i].ModId, ModStat[i].ModName);
        }
    }

   /// MOD_timerRestart(pcheckSystemState, defSysHeartBeatInterVal);
}


void reportHeartBeatToFep()
{
    U32 rc = PARA_VALUE_SYSMONITOR_MODULE_OK;
    CM_SendMsg_1Para(MODULE_INTF, MODULE_SYSMONITOR, MSG_TYPE_COMMAND, CMD_EVENT, CMD_CODE_SYSMONITOR_HEARTBEAT, 0,
        PARA_TYPE_SYSMONITOR_MODULE_STATE,sizeof(U32), (U8*)&rc); 
}

void handleTlvHeartBeat(PCMDHEADER pCmd, U16 ModId)
{
    PPARAM pParam = (PPARAM) (pCmd + 1);
    U32 len = pCmd->cmdLength;
    U32 paraType;
    U32 paraLength;
    U8* paraValue;
    UNPACK_RESULT uprc;
    S32 i = 0;
    while( (uprc=CM_paraUnpackPara(&pParam, &len, &paraType, &paraLength, &paraValue)) == UNPACK_OK )
    {
        switch(paraType)
        {
            case PARA_TYPE_SYSMONITOR_MODULE_STATE:
                if ( PARA_VALUE_SYSMONITOR_MODULE_OK == *(U32 *)paraValue )
                {
                    for ( ; i < MOD_NUM; i++ )
                    {
                        if ( ModStat[i].ModId == ModId )
                        {                                                        
                            ModStat[i].ModOk = true;
                            MONITORLogDebug("modId[0x%x] modName[%s] heartbeat is OK", ModStat[i].ModId,
                                    ModStat[i].ModName);
                            break;
                        }
                    }                     
                }
                else
                {
                    MONITORLogInfo("modId[0x%x] heartbeat is error ", ModId);
                }
				
                if ( ModId == MODULE_INTF )
                {
                    reportHeartBeatToFep();
                }
				
                break;          
            default: 
                break; 

        }

    }
   
    if ( uprc == UNPACK_ERROR )
    {
        MONITORLogError("recv unpack msg from modId[0x%x] ", ModId);
        return;
    }
}


void handleTlvFwmUpgrade(PCMDHEADER pCmd, U16 ModId)
{
    PPARAM pParam = (PPARAM) (pCmd + 1);
    U32 len = pCmd->cmdLength;
    U32 paraType;
    U32 paraLength;
    U8* paraValue;
    UNPACK_RESULT uprc;
    S32 i = 0;
    while( (uprc=CM_paraUnpackPara(&pParam, &len, &paraType, &paraLength, &paraValue)) == UNPACK_OK )
    {
        switch(paraType)
        {
            case PARA_TYPE_FWM_DOWNLOAD_STATE:
                if ( PARA_VALUE_DOWNLOAD_START == *(U32 *)paraValue )
                {
                    downLoadPkg = true;
                }
                else  if ( PARA_VALUE_DOWNLOAD_END == *(U32 *)paraValue )
                {
                    downLoadPkg = false;
                    for ( ; i < MOD_NUM; i++ )
                    {
                        if ( ModStat[i].ModId == ModId )
                        {                                                        
                            ModStat[i].ModOk = true;
                            MONITORLogInfo("download file ok set modId[0x%x] modName[%s] heartbeat is OK", 
                                    ModStat[i].ModId, ModStat[i].ModName);
                            break;
                        }

                    }  
                }
                else 
                {
                    MONITORLogError( "modId[0x%x] heartbeat is error ", ModId);
                }
                break;

            default: 
                break; 

        }

    }
   
    if ( uprc == UNPACK_ERROR )
    {
        MONITORLogError("recv unpack msg from modId[0x%x] ", ModId);
        return;
    }
}

void handleMonTlvCmdEventMsg(U8 *payload, U16 ModId)
{
    PCMDHEADER pCmd = (PCMDHEADER) payload;
    switch( pCmd->cmdCode )
    {
        case CMD_CODE_SYSMONITOR_HEARTBEAT:
            handleTlvHeartBeat(pCmd, ModId);
            break;

        case CMD_CODE_FWM_DOWNING:
            handleTlvFwmUpgrade(pCmd, ModId);
            break;

        case CMD_CODE_SYSMONITOR_STOP:
            MONITORLogNotice("stop feed watchdog");
            feedWTD = false;
            break;

        default:
            MONITORLogNotice( "recv Unknown cmdCode = %x ", pCmd->cmdCode);
            break;
    }

}

void handleMonTlvCmdMsg(U8 *payload, U16 ModId)
{
    PCMDHEADER pCmd = (PCMDHEADER) payload;
    switch( pCmd->cmdType )
    {
        case CMD_EVENT:
            handleMonTlvCmdEventMsg(payload, ModId);
            break;

        default:
            MONITORLogNotice("%s Unknown CmdType = %x ", pCmd->cmdType);
            break;
    }
    
}

void handleTick()
{
    if ( times++ >= CHECKALIVETIME ) 
    {
        checkSystemStateTimer(NULL);
        times = 0;
    }
    if ( feedWTD )
    {
#ifndef MODULE_TEST        
        feedWatchDog();
#endif        
    }
}

void  monitor_tlv_smg_handler()
{
    MSGHEADER msgHdr;
    U8 payload[MAX_MSG_SIZE];
    
    while(1)
    {
        if ( MSG_read(MODULE_SYSMONITOR, &msgHdr, (void*)payload) == RC_OK )
        {
  
    
            if ( MODULE_SYSMONITOR != msgHdr.dstID )
            {
                MONITORLogNotice("received a wrong tlv msg id = %d ",  msgHdr.dstID);
                continue;
            }
    
            switch( msgHdr.msgType )
            {
                case MSG_TYPE_COMMAND:
                    handleMonTlvCmdMsg(payload, msgHdr.srcID) ;               
                    break;

                case MSG_TYPE_TICK:
                    handleTick();
                    break;
             
                default:
                    MONITORLogNotice( "unknown msg type %x ", msgHdr.msgType);
                    break;
            }         
        }
    }

}


static void* monitor_task_pthread (void *arg __attribute__((unused)) )
{
    pthread_setname_np(pthread_self(), "monitor_task" );
#if 0    
    pcheckSystemState = MOD_timerStart(MODULE_SYSMONITOR, defSysHeartBeatInterVal, checkSystemStateTimer, NULL);
    if (pcheckSystemState == NULL)
    {
        dbgprintf(DEBUG_INFO, stdout, " %s creat the monitor timer failed \n", __func__ );
    }
#endif
    MONITORLogNotice(" creat the monitor_task_pthread success " );
    monitor_tlv_smg_handler();
    MONITORLogError( "Exiting the monitor_task_pthread ");
    pthread_exit( (void*)0 );

}


bool initMOnitorTask()
{
   pthread_t thread;
   if ( pthread_create(&thread , NULL, monitor_task_pthread , NULL ) )
   {
       dbgprintf(DEBUG_ERROR, stderr, " %s creat the monitor_task_pthread failed \n", __func__ );
       return false;
   } 
   return true;

}

RESULT Monitor_init(void)
{
#ifndef MODULE_TEST    
    initWatchDog();
#endif    

    MODREG modSysMonitor = { MODULE_SYSMONITOR, true, false, TICK_INTERVAL, (char *)"SysMonitor" };
    if ( RC_OK != MOD_register(&modSysMonitor) )
    {
        return RC_FAIL;
    }

    if ( !initMOnitorTask() )
    {
        return RC_FAIL;
    }

    return RC_OK;
}
